<?php

Raise::load('core.RaiseCollection');
Raise::load('core.RaiseConvert');

/**
 * RaiseNumericCollection
 * a collection of numbers (integer and double)
 *
 * @author Sam-Mauris Yong <hellclanner at live dot com>
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package Raise.Core.DataStructure.Collection
 * @since 1.2
 */
class RaiseNumericCollection extends RaiseCollection {

    /**
     * Return the element of the lowest numeric value in the collection
     * @return integer|double|null Return the lowest value if successful, NULL otherwise.
     */
    public function min(){
        if($this->count() > 0){
            return min($this->_a);
        }else{
            return null;
        }
    }

    /**
     * Return the element of the highest numeric value in the collection
     * @return integer|double|null Return the highest value if successful, NULL otherwise.
     */
    public function max(){
        if($this->count() > 0){
            return max($this->_a);
        }else{
            return null;
        }
    }

    /**
     * Return the average of the numeric collection (sum / count)
     * @return integer|double|null Return the average value if successful, NULL otherwise.
     */
    public function mean(){
        if($this->count() > 0){
            return $this->sum()/$this->count();
        }else{
            return null;
        }
    }

    /**
     * Return the total of the numeric collection
     * @return integer|double|null Return the sum if successful, NULL otherwise.
     */
    public function sum(){
        if($this->count() > 0){
            return array_sum($this->_a);
        }else{
            return null;
        }
    }

    /**
     * Return the mode of the numeric collection
     * @return integer|double|boolean Return the mode value if successful, FALSE otherwise.
     */
    public function mode(){
        if($this->count() > 0){
            $v = array_count_values($this->_a);
            arsort($v);
            $a = new RaiseCollection($v);
            return $a->keys()->first();
        }else{
            return false;
        }
    }

    /**
     * Return the median of the numeric collection
     * @return integer|double|null Return the median value if successful, NULL otherwise.
     */
    public function median(){
        if($this->count() > 0){
            $v = $this->_a;
            rsort($v);
            $a = new RaiseCollection($v);
            $middle = RaiseConvert::toInteger(round($a->count() / 2));
            return $a->get($middle - 1);
        }else{
            return NULL;
        }
    }

    /**
     * Get the range of the numeric collection (max - min)
     * @return integer|double|null Return the range value if successful, NULL otherwise.
     */
    public function range(){
        if($this->count() > 0){
            return $this->max() - $this->min();
        }else{
            return NULL;
        }
    }

    /**
     * Return the probability of randomly selecting a particular value from the collection
     * @param integer|double $value
     * @return double|null Return the probability (0.0 to 1.0) if successful, NULL otherwise.
     */
    public function probability($value){
        if($this->count() > 0){
            $v = array_count_values($this->_a);
            $a = new RaiseCollection($v);
            return $a->get($value) / $this->count();
        }else{
            return NULL;
        }
    }

    /**
     * Calculate the standard deviation of the numeric collection
     * @param bool $sample Sets whether the statistics is sample (True) or population (False)
     * @return double|NULL The value of the standard deviation of the numeric collection if successful, NULL otherwise.
     */
    public function standardDeviation($sample = false){
        if($this->count() > 0){
            $mean = $this->mean();
            $var = 0.0;
            foreach ($this->_a as $i)
            {
                $var += pow($i - $mean, 2);
            }
            $var /= ($sample ? $this->count() - 1 : $this->count());
            return RaiseConvert::toDouble(sqrt($var));
        }else{
            return NULL;
        }
    }

}
